package com.yahoo.labs.snow;

import it.unimi.dsi.fastutil.ints.Int2DoubleOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.io.FileLinesCollection.FileLinesIterator;

import java.io.File;

import cern.colt.matrix.impl.SparseDoubleMatrix2D;

import com.yahoo.labs.snow.util.FileUtils;
import com.yahoo.labs.snow.util.MatrixUtils;

/**
 * A problem instance from which snowflakes will be extracted.
 * 
 * @author chato
 *
 */
public class ProblemInstance {
	/**
	 * The budget available.
	 */
	private final double budget;
	/**
	 * Cost of adding a node to the solution.
	 */
	protected Int2DoubleOpenHashMap nodeCost;
	/**
	 * Compatibility between two nodes.
	 */
	protected SparseDoubleMatrix2D nodeCompat;
	/**
	 * Attribute to be covered.
	 */
	protected Int2ObjectOpenHashMap<IntOpenHashSet> nodeCover;
	/**
	 * Identifiers of nodes.
	 */
	private Object2IntOpenHashMap<String> node2id;
	private Int2ObjectOpenHashMap<String> id2node;
	
	ProblemInstance(double budget) { 
		this.budget = budget;
	}

	public IntSet getIds() {
		return nodeCost.keySet();
	}

	public int numNodes() {
		return nodeCost.size();
	}

	public double getCost(int id) {
		return nodeCost.get(id);
	}

	public double getBudget() {
		return budget;
	}

	public IntOpenHashSet getCover(int id) {
		return nodeCover.get(id);
	}
	
	public double getCompat(int id1, int id2) {
		return nodeCompat.getQuick(id1, id2);
	}
	
	/**
	 * Compatibility between two clusters.
	 * 
	 * @param set1 the first cluster
	 * @param set2 the second cluster
	 * @return the maximum of compat(n1,n2) for n1 in set1, n2 in set2
	 */
	public double maxPairwiseCompatibility(IntOpenHashSet set1, IntOpenHashSet set2) {
		double maxCompatibility = 0.0;
		for (int n1 : set1) {
			for (int n2 : set2) {
				if (getCompat(n1, n2) > maxCompatibility) {
					maxCompatibility = getCompat(n1, n2);
				}
			}
		}
		return maxCompatibility;
	}
	
	public double maxPairwiseCompatibility(Snowflake s1, Snowflake s2) {
		return maxPairwiseCompatibility(s1.elements, s2.elements);
	}
	
	public SparseDoubleMatrix2D getCompat() {
		return nodeCompat;
	}
	
	public String getNode(int id) {
		return id2node.get(id);
	}
	
	public int getId(String node) {
		return node2id.getInt(node);
	}
	
	protected void createIdNodeMappings(File file) {
		node2id = new Object2IntOpenHashMap<String>();
		node2id.defaultReturnValue(-1);
		id2node = new Int2ObjectOpenHashMap<String>();
		FileLinesIterator it = FileUtils.getIterator(file);
		int nextId = 0;
		while( it.hasNext() ) {
			String[] tokens = (it.next()).toString().split("\t", 2);
			String node = tokens[0];
			if( node2id.containsKey(node) ) {
				throw new IllegalArgumentException("Repeated node: " + node );
			}
			node2id.put(node, nextId);
			id2node.put(nextId, node);
			nextId++;
		}
	}

	/**
	 * Divides each compatibility by the maximum compatibility value
	 */
	public void normalizeNodeCompat() {
		final double maxValue = MatrixUtils.max(nodeCompat);
		MatrixUtils.scalarMultiply(nodeCompat, 1.0/maxValue);
	}
}